package com.gitee.feizns.dynamic.convert.impl;

import com.gitee.feizns.dynamic.convert.AbstractConverter;
import com.gitee.feizns.dynamic.reflect.ClassUtils;
import com.gitee.feizns.dynamic.reflect.MethodUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Deque;

/**
 * <b>方法转换器实现类</b>
 * <p>
 *     会在目标类型中查找一个单参数的静态方法，
 *     该方法是原数据（source）可以访问的方法
 *     （仅支持非基础类型方法）
 * </p>
 * <pre>{@code
 *  static class User {
 *     int id;
 *     public String toString() { return getClass().getSimpleName() + ":[id=" + id + "]"; }
 *     public static User valueOf(Object o) {
 *         return new User();
 *     }
 * }
 *
 * static class Admin {
 *     int id;
 *     public String toString() { return getClass().getSimpleName() + ":[id=" + id + "]"; }
 *     public static Admin valueOf(User o) {
 *         Admin admin = new Admin();
 *         admin.id = o.id;
 *         return admin;
 *     }
 * }
 *
 * public static void main(String[] args) {
 *     SingleParameterMethodConverterHandler handler = new SingleParameterMethodConverterHandler("valueOf");
 *     User u = new User();
 *     System.out.println(u);                                          //User:[id=0]
 *     u.id = 32;
 *     System.out.println(u);                                          //User:[id=32]
 *     Class<?> adminClass = Admin.class;
 *     System.out.println(handler.to(u, (Class<Object>) adminClass));  //Admin:[id=32]
 * }
 * }</pre>
 * @author feizns
 * @since 2022/11/17
 */
public class SingleParameterStaticMethodConverter extends AbstractConverter<Object, Object> {

    /**
     * 转换的方法名（仅支持非基础类型方法）
     */
    private final String staticMethodName;

    public SingleParameterStaticMethodConverter(String staticMethodName) {
        this.staticMethodName = staticMethodName;
    }

    @Override
    public Object to(Object source, Class<Object> targetType) {
        if ( source != null && !targetType.isAssignableFrom(source.getClass()) ) {
            Method canInvokeStaticMethod = findCanInvokeStaticMethod(targetType, staticMethodName, source.getClass());
            if ( canInvokeStaticMethod != null )
                return MethodUtils.invokeStatic(canInvokeStaticMethod, source);
            return null;
        }
        return source;
    }

    @Override
    public boolean support(Object source, Class<?> targetType) {
        if ( source != null )
            return findCanInvokeStaticMethod(targetType, staticMethodName, source.getClass()) != null;
        return false;
    }

    /**
     * <b>从{@code type}的继承树中查找可以访问的方法<b/>
     * <p>
     *     在type类中查找从parameterType可以访问的方法
     * </p>
     * @see #findCanInvokeStaticMethodFromType(Class, String, Class)
     * @param type 需要查找方法的类（会查找该类的父类及接口）
     * @param methodName 静态方法名称
     * @param parameterType 查找的参数类型
     * @return 最近匹配通过的方法（匹配规则：当前类 -> 父类 -> 当前类接口 -> 父类接口）
     * @throws NullPointerException 如果type或者methodName为空将抛出该异常
     */
    public static Method findCanInvokeStaticMethod(Class<?> type, String methodName, Class<?> parameterType) {
        //匹配规则为 当前类 -> 父类 -> 当前类接口 -> 父类接口
        Deque<Class<?>> classesAndInterfaces = ClassUtils.getClassesAndInterfaces(type);
        while ( !classesAndInterfaces.isEmpty() ) {
            Class<?> first = classesAndInterfaces.removeFirst();
            Method canInvokeMethodFromType = findCanInvokeStaticMethodFromType(first, methodName, parameterType);
            if ( canInvokeMethodFromType != null )
                return canInvokeMethodFromType;
        }
        return null;
    }

    /**
     * <b>从{@code type}中获取参数{@code parameterType}类型可以访问的静态方法<b/>
     * <p>
     *     在type类中查找从parameterType可以访问的方法
     * </p>
     * <pre>{@code
     *  //User类中有一个valueOf(Object o)和valueOf(String o)
     * static class User {
     *     public static void valueOf(Object o) {}
     *     public static void valueOf(String o) {}
     * }
     * public static void main(String[] args) throws NoSuchMethodException {
     *     Method valueOf = findCanInvokeMethod(User.class, "valueOf", Integer.class);
     *     System.out.println(valueOf);//查找到可以使用Integer类型访问的方法User.valueOf(java.lang.Object)
     *     //JDK原生方法查找，会报找不到的异常
     *     User.class.getMethod("valueOf", Integer.class); //java.lang.NoSuchMethodException
     * }
     * }</pre>
     * @param type 需要查找方法的类
     * @param methodName 静态方法名称
     * @param parameterType 查找的参数类型
     * @return 最近匹配通过的方法（匹配规则：当前类 -> 父类 -> 当前类接口 -> 父类接口）
     * @throws NullPointerException 如果type或者methodName为空将抛出该异常
     */
    public static Method findCanInvokeStaticMethodFromType(Class<?> type, String methodName, Class<?> parameterType) {
        //匹配规则为 当前类 -> 父类 -> 当前类接口 -> 父类接口
        Deque<Class<?>> classesAndInterfaces = ClassUtils.getClassesAndInterfaces(parameterType);
        while ( !classesAndInterfaces.isEmpty() ) {
            Class<?> first = classesAndInterfaces.removeFirst();
            Method method = MethodUtils.find(type, methodName, first);
            //保证找到方法且该方法为静态方法且返回值为type类型
            if ( method != null && method.getReturnType() == type && Modifier.isStatic(method.getModifiers()) )
                return method;
        }
        return null;
    }

}
